package com.atguigu.daijia.map.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.repository.OrderServiceLocationRepository;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DriverInfoFeignClient driverInfoFeignClient;

    @Autowired
    private OrderServiceLocationRepository orderServiceLocationRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;

    //司机开启接单，更新司机位置信息
    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        //把司机的位置信息添加到redis geo中
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue(),
                                updateDriverLocationForm.getLatitude().doubleValue());
        stringRedisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION,point,updateDriverLocationForm.getDriverId().toString());
        return true;
    }

    //司机关闭接单，删除司机位置信息
    @Override
    public Boolean removeDriverLocation(Long driverId) {
        stringRedisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION,driverId.toString());
        return true;
    }

    //搜索附近满足条件的司机
    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm searchNearByDriverForm) {
        //搜索经纬度位置5km以内的司机
        //操作redis,查询geo里符合条件的数据,返回list集合
        //创建circle对象
        Point point = new Point(searchNearByDriverForm.getLongitude().doubleValue(), searchNearByDriverForm.getLatitude().doubleValue());
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS);
        Circle circle = new Circle(point,distance);
        //定义GEO参数,设置返回结果里包含的内容
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance()      //包含距离
                .includeCoordinates()   //包含坐标
                .sortAscending();       //升序
        //操作redis
        GeoResults<RedisGeoCommands.GeoLocation<String>> result = stringRedisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = result.getContent();
        //对查询list集合进行处理,遍历list集合,得到每个司机的信息
        List<NearByDriverVo> driverList = new ArrayList<>();
        //根据每个司机的个性化设置进行判断
        content.stream().forEach(item -> {
            //获取司机id
            Long driverId = Long.parseLong(item.getContent().getName());
            //远程调用获取司机个性化设置
            Result<DriverSet> driverSetResult = driverInfoFeignClient.getDriverSet(driverId);
            DriverSet driverSet = driverSetResult.getData();
            if(driverSet == null) return;
            //判断订单里程order_distance,为0表示没有限制,其余表示只接订单里程<=orderDistance的订单
            BigDecimal orderDistance = driverSet.getOrderDistance();
            if(orderDistance.doubleValue()!=0  && orderDistance.subtract(searchNearByDriverForm.getMileageDistance()).doubleValue() < 0){
                return;
            }
            //判断接单里程accept_distance,为0表示没有限制,其余表示只接接单距离(司机与乘客的距离)在acceptDistance范围内的订单
            BigDecimal acceptDistance = driverSet.getAcceptDistance();
            //获取当前接单距离(司机与乘客的距离)
            BigDecimal curDistance = new BigDecimal(item.getDistance().getValue()).setScale(2, RoundingMode.HALF_UP);
            if(acceptDistance.doubleValue() != 0 && acceptDistance.subtract(curDistance).doubleValue() < 0){
                return;
            }
            //封装符合条件的数据
            NearByDriverVo nearByDriverVo = new NearByDriverVo();
            nearByDriverVo.setDriverId(driverId);
            nearByDriverVo.setDistance(curDistance);
            driverList.add(nearByDriverVo);
        });
        return driverList;
    }

    //司机赶往代驾起始点：更新司机实时地址到redis
    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {
        //封装经纬度
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
        String key = RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId();
        stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(orderLocationVo));
        return true;
    }

    //司机赶往代驾起始点：乘客获取司机经纬度位置
    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        String key = RedisConstant.UPDATE_ORDER_LOCATION + orderId;
        String json = stringRedisTemplate.opsForValue().get(key);
        OrderLocationVo orderLocationVo = JSONObject.parseObject(json, OrderLocationVo.class);
        return orderLocationVo;
    }

    //批量保存代驾服务订单位置
    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderLocationServiceFormList) {
        //OrderServiceLocationForm --> OrderServiceLocation
        List<OrderServiceLocation> list = orderLocationServiceFormList.stream().map(orderServiceLocationForm -> {
            OrderServiceLocation orderServiceLocation = BeanUtil.copyProperties(orderServiceLocationForm, OrderServiceLocation.class);
            orderServiceLocation.setId(ObjectId.get().toString());
            orderServiceLocation.setCreateTime(new Date());
            return orderServiceLocation;
        }).collect(Collectors.toList());
        //将list集合存入mongodb中
        orderServiceLocationRepository.saveAll(list);
        return true;
    }

    //代驾服务：获取订单服务最后一个位置信息(乘客端)
    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        //查询MongoDB,条件：orderId,根据时间降序排序取最新的一条
        //封装条件
        Query query = new Query();
        query.addCriteria(Criteria.where("orderId").is(orderId));
        query.with(Sort.by(Sort.Order.desc("createTime")));
        query.limit(1);
        //查询MongoDB
        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
        //类型转换
        OrderServiceLastLocationVo orderServiceLastLocationVo = BeanUtil.copyProperties(orderServiceLocation, OrderServiceLastLocationVo.class);
        return orderServiceLastLocationVo;
    }

    //计算订单的实际里程
    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {
        //1.根据订单id获取订单位置信息，根据创建时间排序(升序)
        //封装条件对象
        OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
        orderServiceLocation.setOrderId(orderId);
        Example<OrderServiceLocation> example = Example.of(orderServiceLocation);
        //创建排序规则
        Sort sort = Sort.by(Sort.Direction.ASC,"createTime");
        //查询MongoDB
        List<OrderServiceLocation> list = orderServiceLocationRepository.findAll(example, sort);
        //2.遍历集合,计算相邻两点之间的距离，计入总距离
        double realDistance = 0.0;
        if(CollUtil.isNotEmpty(list) ){
            for(int i=0;i<list.size()-1;i++){
                OrderServiceLocation location1 = list.get(i);
                OrderServiceLocation location2 = list.get(i + 1);
                double distance = LocationUtil.getDistance(location1.getLatitude().doubleValue(), location1.getLongitude().doubleValue(),
                        location2.getLatitude().doubleValue(), location2.getLongitude().doubleValue());
                realDistance += distance;
            }
        }

        //这里为了测试，模拟数据为：预估距离+5km
        if(realDistance == 0){
            return orderInfoFeignClient.getOrderInfo(orderId).getData().getExpectDistance().add(new BigDecimal("5"));
        }

        //3.返回实际里程
        return new BigDecimal(realDistance);
    }
}
